/**
 * Sql生成器。
 */
class SqlBuilder {

    /**
     * 构造函数。
     */
    constructor(params) {
        this.sorts = [];
        this.segments = [];
        this._hasWhere = false;

        this.skipsNumber = 1;
        this.takesNumber = 10;
        this.enablePaged = false;

        this.params = params;
        this.paramNames = Object.getOwnPropertyNames(params);
    }

    /**
     * 是否已经包含where
     */
    hasWhere() {
        this._hasWhere = true;

        return this;
    }

    /**
     * 追加and查询条件
     * @param {String} segment 查询片段
     * @param {Object | Function} queryable 参与查询的条件(值或者有返回值的函数)
     */
    and(segment, queryable) {
        if(!queryable){
            let properties = this._getProperties(segment);

            queryable = properties.length > 0 && properties.every(element=>!!element);
        }

        this.segments.push({
            segment,
            queryable,
            join: 'and'
        });

        return this;
    }

    /**
     * 追加or查询条件
     * @param {String} segment 查询片段
     * @param {Object | Function} queryable 参与查询的条件(值或者有返回值的函数)
     */
    or(segment, queryable) {
        if(!queryable){
            let properties = this._getProperties(segment);

            queryable = properties.length > 0 && properties.every(element=>!!element);
        }

        this.segments.push({
            segment,
            queryable,
            join: 'or'
        });

        return this;
    }

    /**
     * 追加升序排序
     * @param {String} column 排序字段
     */
    asc(column) {
        this.sorts.push({
            column,
            by: 'asc'
        });

        return this;
    }

    /**
     * 追加倒序排序
     * @param {String} column 排序字段
     */
    desc(column) {
        this.sorts.push({
            column,
            by: 'desc'
        });

        return this;
    }

    /**
     * 启用分页
     */
    paging() {
        let pgIndex = (parseInt(this._getParameter("PageIndex")) || 1);
        let pgSize = (parseInt(this._getParameter("PageSize")) || 10);

        this.enablePaged = true;
        this.takesNumber = pgSize < 1 ? 10 : pgSize;

        this.skipsNumber = ((pgIndex < 1 ? 1 : pgIndex) - 1) * this.takesNumber;

        return this;
    }

    /**
     * 生成sql脚本
     */
    build() {
        let params = [];
        let sqlSegment = '';

        for (let index = 0; index < this.segments.length; index++) {
            let element = this.segments[index];
            let queryable = typeof element.queryable == 'function' ? element.queryable.call(this.params, this.params) : element.queryable;

            if (queryable) {
                if (sqlSegment == '') {
                    sqlSegment += this._hasWhere ? element.join : '';
                } else {
                    sqlSegment += element.join;
                }

                sqlSegment += ` ${element.segment} `;
            }
        }

        if (sqlSegment != '') {
            sqlSegment = this._hasWhere ? '' : ' where ' + sqlSegment;
        }

        if (this.sorts.length > 0) {
            sqlSegment += ' ORDER BY ';
            let orders = [];

            this.sorts.forEach(element => {
                orders.push(` ${element.column} ${element.by}`);
            });

            sqlSegment += orders.join(',');
        }

        if (this.enablePaged) {
            sqlSegment += ` LIMIT ${this.skipsNumber}, ${this.takesNumber}`;
        }

        return sqlSegment;
    }

    /**
     * 获取参数值
     * @param {String} propName 
     */
    _getParameter(propName) {
        let prop = this.paramNames.find(p => p.toLowerCase() == propName.toLowerCase());

        if (prop) {
            return this.params[prop];
        }

        return null;
    }

    /**
     * 获取查询片段中含有的属性值。
     * @param {String} segment 
     * @returns 属性值列表
     */
    _getProperties(segment) {
        let properties = [];
        let matches = segment.match(/\@(\w+)/ig);

        if (matches) {
            matches.forEach(element => {
                let propName = this.paramNames.find(p => p.toLowerCase() == element.substring(1).toLowerCase());

                if (propName) {
                    properties.push(this.params[propName]);
                }
            });
        }

        return properties;
    }
}

module.exports = SqlBuilder;